Étude du modèle RFM : Analyse du comportement client


1.1 Introduction


Dans cet article, nous abordons la problématique de l’évaluation client. Nous mettrons en évidence les raisons pour lesquelles il est important pour une entreprise de connaître les habitudes de consommation de ses clients. À travers l’étude d’un cas pratique, nous présenterons les étapes permettant de segmenter une base client afin d’analyser leur comportement de consommation.

La RFM (Recency, Frequency, Monetary Analysis), ou analyse RFM, est une méthode de segmentation et de ciblage comportemental qui permet aux entreprises de classer et de segmenter leurs clients en fonction de trois critères : la récence, la fréquence et le montant des transactions. Cette approche aide les marketeurs et les dirigeants de PME à identifier leur audience cible et à optimiser l’utilisation de leur budget marketing.


1.2 Présentation des 3 facteurs cible d’une analyse RFM


Cette méthode attribue des notes aux clients en fonction de 3 facteurs :

  • Récence : la récence correspond à la date du dernier achat des clients. Ceux qui en ont effectué un récemment, généralement au cours des dernières semaines, ont toujours le produit et la marque en tête. Ils sont donc plus susceptibles d’effectuer un nouvel achat. Vous pouvez mesurer la récence si c’est pertinent dans votre cas. Néanmoins, il est important de noter que pour certaines entreprises, les clients ne commandent pas tous les quatre matins. Par exemple, un constructeur automobile peut vendre une seule voiture à quelqu’un en dix ans.

  • Fréquence : il s’agit de la fréquence à laquelle le client réalise des transactions, ce qui peut vous aider à identifier les clients réguliers. Par exemple, nombreux sont ceux qui effectuent des achats récurrents et fréquents dans un délai défini. C’est un critère essentiel pour déterminer les personnes les plus susceptibles de choisir à nouveau votre marque après une première acquisition.

  • Montant : cet élément fait référence à la valeur monétaire qu’un client dépense sur une période donnée. Il est toujours important d’en tenir compte, car cela peut vous donner des indications sur le comportement des consommateurs. Par exemple, vous constaterez peut-être que les clients présentant le montant le plus élevé n’achètent pas d’articles aussi souvent que les autres, mais acquièrent généralement les produits les plus chers.

Les données de chaque facteur permettent aux entreprises de fournir une analyse objective et de déterminer quelle audience cibler pour optimiser leurs campagnes publicitaires et marketing. La plupart des entreprises utilisent une échelle comprise entre 1 et 5, mais vous pouvez choisir toutes les valeurs qui vous semblent nécessaires et utiles pour évaluer vos clients.


Exploration de la segmentation client avec l’analyse RFM et le regroupement K-Means


2. Exploration et traitement des données


2.1 Collecte et Netoyage des données

J’ai utilisé les données de vente en ligne de Kaggle, qui comprennent différentes colonnes telles que le numéro de facture, la date de la facture, l’identifiant client, le prix de vente, la quantité, etc. Ces données seront très utiles pour l’analyse. Voici les données de vente en ligne : onlineRetailData.

# Charger les bibliothèques nécessaires 
suppressMessages(library(dplyr))        # Manipulation de données
suppressMessages(library(data.table))   # Alternative pour la manipulation de données
suppressMessages(library(ggplot2))      # Visualisation
suppressMessages(library(lubridate))    # Manipulation des dates et heures
suppressMessages(library(caret))        # Modélisation et apprentissage automatique
suppressMessages(library(cluster))      # Clustering et silhouette
Retail_DF <- read.csv("~/Desktop/TD R/OnlineRetail.csv", sep = ",")
head(Retail_DF, 5)
##   InvoiceNo StockCode                         Description Quantity
## 1    536365    85123A  WHITE HANGING HEART T-LIGHT HOLDER        6
## 2    536365     71053                 WHITE METAL LANTERN        6
## 3    536365    84406B      CREAM CUPID HEARTS COAT HANGER        8
## 4    536365    84029G KNITTED UNION FLAG HOT WATER BOTTLE        6
## 5    536365    84029E      RED WOOLLY HOTTIE WHITE HEART.        6
##      InvoiceDate UnitPrice CustomerID        Country
## 1 12/1/2010 8:26      2.55      17850 United Kingdom
## 2 12/1/2010 8:26      3.39      17850 United Kingdom
## 3 12/1/2010 8:26      2.75      17850 United Kingdom
## 4 12/1/2010 8:26      3.39      17850 United Kingdom
## 5 12/1/2010 8:26      3.39      17850 United Kingdom
# Supprimer les lignes où Quantity ou UnitPrice est négatif
Retail_DF <- Retail_DF[Retail_DF$Quantity >= 0 & Retail_DF$UnitPrice >= 0, ]

# Vérifier le DataFrame après suppression
head(Retail_DF, 5)
##   InvoiceNo StockCode                         Description Quantity
## 1    536365    85123A  WHITE HANGING HEART T-LIGHT HOLDER        6
## 2    536365     71053                 WHITE METAL LANTERN        6
## 3    536365    84406B      CREAM CUPID HEARTS COAT HANGER        8
## 4    536365    84029G KNITTED UNION FLAG HOT WATER BOTTLE        6
## 5    536365    84029E      RED WOOLLY HOTTIE WHITE HEART.        6
##      InvoiceDate UnitPrice CustomerID        Country
## 1 12/1/2010 8:26      2.55      17850 United Kingdom
## 2 12/1/2010 8:26      3.39      17850 United Kingdom
## 3 12/1/2010 8:26      2.75      17850 United Kingdom
## 4 12/1/2010 8:26      3.39      17850 United Kingdom
## 5 12/1/2010 8:26      3.39      17850 United Kingdom
# Filtrer les anomalies où Quantity ou UnitPrice est négatif
anomalies <-Retail_DF[Retail_DF$Quantity < 0 | Retail_DF$UnitPrice < 0, ]

# Afficher les anomalies
print(anomalies)
## [1] InvoiceNo   StockCode   Description Quantity    InvoiceDate UnitPrice  
## [7] CustomerID  Country    
## <0 lignes> (ou 'row.names' de longueur nulle)
summary(anomalies)
##   InvoiceNo          StockCode         Description           Quantity  
##  Length:0           Length:0           Length:0           Min.   : NA  
##  Class :character   Class :character   Class :character   1st Qu.: NA  
##  Mode  :character   Mode  :character   Mode  :character   Median : NA  
##                                                           Mean   :NaN  
##                                                           3rd Qu.: NA  
##                                                           Max.   : NA  
##  InvoiceDate          UnitPrice     CustomerID    Country         
##  Length:0           Min.   : NA   Min.   : NA   Length:0          
##  Class :character   1st Qu.: NA   1st Qu.: NA   Class :character  
##  Mode  :character   Median : NA   Median : NA   Mode  :character  
##                     Mean   :NaN   Mean   :NaN                     
##                     3rd Qu.: NA   3rd Qu.: NA                     
##                     Max.   : NA   Max.   : NA
Retail_DF <- na.omit(Retail_DF)

# Afficher les données nettoyées
head(Retail_DF, 5)
##   InvoiceNo StockCode                         Description Quantity
## 1    536365    85123A  WHITE HANGING HEART T-LIGHT HOLDER        6
## 2    536365     71053                 WHITE METAL LANTERN        6
## 3    536365    84406B      CREAM CUPID HEARTS COAT HANGER        8
## 4    536365    84029G KNITTED UNION FLAG HOT WATER BOTTLE        6
## 5    536365    84029E      RED WOOLLY HOTTIE WHITE HEART.        6
##      InvoiceDate UnitPrice CustomerID        Country
## 1 12/1/2010 8:26      2.55      17850 United Kingdom
## 2 12/1/2010 8:26      3.39      17850 United Kingdom
## 3 12/1/2010 8:26      2.75      17850 United Kingdom
## 4 12/1/2010 8:26      3.39      17850 United Kingdom
## 5 12/1/2010 8:26      3.39      17850 United Kingdom
summary(Retail_DF)
##   InvoiceNo          StockCode         Description           Quantity       
##  Length:397924      Length:397924      Length:397924      Min.   :    1.00  
##  Class :character   Class :character   Class :character   1st Qu.:    2.00  
##  Mode  :character   Mode  :character   Mode  :character   Median :    6.00  
##                                                           Mean   :   13.02  
##                                                           3rd Qu.:   12.00  
##                                                           Max.   :80995.00  
##  InvoiceDate          UnitPrice          CustomerID      Country         
##  Length:397924      Min.   :   0.000   Min.   :12346   Length:397924     
##  Class :character   1st Qu.:   1.250   1st Qu.:13969   Class :character  
##  Mode  :character   Median :   1.950   Median :15159   Mode  :character  
##                     Mean   :   3.116   Mean   :15294                     
##                     3rd Qu.:   3.750   3rd Qu.:16795                     
##                     Max.   :8142.750   Max.   :18287

Anomalie est une dataframe vide ce qui montre que notre table à été correctement filtrer et netoyer des données éronnées et manquantes.

2.2 Data Exploration

Dans ce jeu de données, le prix total du produit n’est pas mentionné, il est séparé en quantité et prix unitaire. J’ai ajouté une nouvelle colonne nommée “Total_price” qui contient le résultat de la multiplication de la quantité par le prix unitaire, ce qui donne le prix total.

Ici, je voulais voir comment les montants sont répartis par clients en regroupant les montants par customerID. La variation est très élevée, c’est pourquoi j’ai décidé de normaliser les données pour obtenir des résultats plus précis et une meilleure visualisation des résultats dans un graphique.

Retail_DF$Total_price <- Retail_DF$Quantity * Retail_DF$UnitPrice
head(Retail_DF, 5) 
##   InvoiceNo StockCode                         Description Quantity
## 1    536365    85123A  WHITE HANGING HEART T-LIGHT HOLDER        6
## 2    536365     71053                 WHITE METAL LANTERN        6
## 3    536365    84406B      CREAM CUPID HEARTS COAT HANGER        8
## 4    536365    84029G KNITTED UNION FLAG HOT WATER BOTTLE        6
## 5    536365    84029E      RED WOOLLY HOTTIE WHITE HEART.        6
##      InvoiceDate UnitPrice CustomerID        Country Total_price
## 1 12/1/2010 8:26      2.55      17850 United Kingdom       15.30
## 2 12/1/2010 8:26      3.39      17850 United Kingdom       20.34
## 3 12/1/2010 8:26      2.75      17850 United Kingdom       22.00
## 4 12/1/2010 8:26      3.39      17850 United Kingdom       20.34
## 5 12/1/2010 8:26      3.39      17850 United Kingdom       20.34
# Agréger les données en sommant les 'Total_price' par 'CustomerID'

monetary <- Retail_DF %>%
  group_by(CustomerID) %>%
  summarise(Total_price = sum(Total_price, na.rm = TRUE)) %>%
  arrange(Total_price)  # Trier en ordre croissant

# Afficher les premières lignes
head(monetary)
## # A tibble: 6 × 2
##   CustomerID Total_price
##        <int>       <dbl>
## 1      13256        0   
## 2      16738        3.75
## 3      14792        6.2 
## 4      16454        6.9 
## 5      17956       12.8 
## 6      16878       13.3
tail(monetary)  # Affiche les dernières lignes de la table des monetry
## # A tibble: 6 × 2
##   CustomerID Total_price
##        <int>       <dbl>
## 1      12415     124915.
## 2      14911     143825.
## 3      16446     168472.
## 4      17450     194551.
## 5      18102     259657.
## 6      14646     280206.
summary(monetary)
##    CustomerID     Total_price      
##  Min.   :12346   Min.   :     0.0  
##  1st Qu.:13812   1st Qu.:   307.2  
##  Median :15299   Median :   674.5  
##  Mean   :15300   Mean   :  2053.8  
##  3rd Qu.:16778   3rd Qu.:  1661.6  
##  Max.   :18287   Max.   :280206.0

Nous pouvons remarquer le présence d’articles avec un prix à 0, ce qui pourrait correspondre à des promotions ou des retours d’articles. Malheureusement, nous avons pas plus de détail mais cela peut àvoir des éffets sur nos graphiques vu le nombres d’articles concerné. Nous allons donc les supprimer.

Retail_DF <- Retail_DF[Retail_DF$UnitPrice > 0, ]

monetary <- monetary[monetary$Total_price > 0, ]


summary(Retail_DF)
##   InvoiceNo          StockCode         Description           Quantity       
##  Length:397884      Length:397884      Length:397884      Min.   :    1.00  
##  Class :character   Class :character   Class :character   1st Qu.:    2.00  
##  Mode  :character   Mode  :character   Mode  :character   Median :    6.00  
##                                                           Mean   :   12.99  
##                                                           3rd Qu.:   12.00  
##                                                           Max.   :80995.00  
##  InvoiceDate          UnitPrice          CustomerID      Country         
##  Length:397884      Min.   :   0.001   Min.   :12346   Length:397884     
##  Class :character   1st Qu.:   1.250   1st Qu.:13969   Class :character  
##  Mode  :character   Median :   1.950   Median :15159   Mode  :character  
##                     Mean   :   3.116   Mean   :15294                     
##                     3rd Qu.:   3.750   3rd Qu.:16795                     
##                     Max.   :8142.750   Max.   :18287                     
##   Total_price       
##  Min.   :     0.00  
##  1st Qu.:     4.68  
##  Median :    11.80  
##  Mean   :    22.40  
##  3rd Qu.:    19.80  
##  Max.   :168469.60
summary(monetary)
##    CustomerID     Total_price       
##  Min.   :12346   Min.   :     3.75  
##  1st Qu.:13813   1st Qu.:   307.42  
##  Median :15300   Median :   674.48  
##  Mean   :15300   Mean   :  2054.27  
##  3rd Qu.:16779   3rd Qu.:  1661.74  
##  Max.   :18287   Max.   :280206.02

2.3 Calcule et rôle des métriques RFM

Pour comprendre le comportement des clients, les métriques RFM jouent un rôle essentiel, car la fréquence et la valeur monétaire influencent la valeur à vie d’un client, tandis que la récence a un impact sur la rétention, une mesure de l’engagement. Ici, je réalise une analyse approfondie de la méthode RFM combinée avec le regroupement K-Means, car elle permet de répondre à des questions cruciales telles que : “Qui sont les meilleurs clients ?” ou “Quels clients contribuent au taux d’attrition ?”, etc.

Dans cette analyse, je calcule la fréquence des clients en comptant le nombre de factures (Invoice numbers) pour chaque client. Plus ce nombre est élevé, plus le client achète souvent dans le magasin.

# Calculer la fréquence des achats (nombre de factures) par client
frequency <- aggregate(InvoiceNo ~ CustomerID, data = Retail_DF, length)
# Afficher les premières lignes du résultat
frequency <- frequency[order(desc(frequency$InvoiceNo)),]
head(frequency)
##      CustomerID InvoiceNo
## 4011      17841      7847
## 1880      14911      5675
## 1290      14096      5111
## 327       12748      4595
## 1662      14606      2700
## 2177      15311      2379
tail(frequency)
##      CustomerID InvoiceNo
## 4210      18113         1
## 4225      18133         1
## 4255      18174         1
## 4263      18184         1
## 4302      18233         1
## 4325      18268         1
Retail_DF$InvoiceDate <- lubridate::mdy_hm(Retail_DF$InvoiceDate)
head(Retail_DF$InvoiceDate)
## [1] "2010-12-01 08:26:00 UTC" "2010-12-01 08:26:00 UTC"
## [3] "2010-12-01 08:26:00 UTC" "2010-12-01 08:26:00 UTC"
## [5] "2010-12-01 08:26:00 UTC" "2010-12-01 08:26:00 UTC"
# Calcul de la différence entre la date maximale et chaque date
max_date <- max(Retail_DF$InvoiceDate, na.rm = TRUE)
Retail_DF$Diff <- as.numeric(difftime(max_date, Retail_DF$InvoiceDate, units = "days"))
# Calcul de la récence minimale (Diff minimale) pour chaque client
recency <- Retail_DF %>%
  group_by(CustomerID) %>%
  summarise(Diff = min(Diff, na.rm = TRUE)) %>%
  ungroup()

# Afficher les premières lignes de la table des récences
head(recency)
## # A tibble: 6 × 2
##   CustomerID   Diff
##        <int>  <dbl>
## 1      12346 325.  
## 2      12347   1.87
## 3      12348  75.0 
## 4      12349  18.1 
## 5      12350 310.  
## 6      12352  35.9
summary(recency)
##    CustomerID         Diff       
##  Min.   :12346   Min.   :  0.00  
##  1st Qu.:13813   1st Qu.: 17.07  
##  Median :15300   Median : 50.09  
##  Mean   :15300   Mean   : 92.05  
##  3rd Qu.:16779   3rd Qu.:141.73  
##  Max.   :18287   Max.   :373.12

Ici, nous calculons la récence en soustrayant la date la plus récente de la dernière date de transaction des clients. Nous calculons la valeur monétaire en additionnant tous les montants associés à chaque client. Enfin, nous fusionnons toutes les colonnes dans un seul DataFrame.

RFM <- merge(frequency,recency, by = "CustomerID")
RFM <- merge(RFM, monetary, by = "CustomerID")
# Renommer les colonnes du DataFrame RFM
colnames(RFM) <- c("CustomerID", "frequency", "recency", "monetary")
head(RFM)
##   CustomerID frequency    recency monetary
## 1      12346         1 325.117361 77183.60
## 2      12347       182   1.873611  4310.00
## 3      12348        31  74.984028  1797.24
## 4      12349        73  18.124306  1757.55
## 5      12350        17 309.867361   334.40
## 6      12352        85  35.925694  2506.04
summary(frequency)  # Voir la distribution complète
##    CustomerID      InvoiceNo      
##  Min.   :12346   Min.   :   1.00  
##  1st Qu.:13813   1st Qu.:  17.00  
##  Median :15300   Median :  41.00  
##  Mean   :15300   Mean   :  91.72  
##  3rd Qu.:16779   3rd Qu.: 100.00  
##  Max.   :18287   Max.   :7847.00
summary(recency)
##    CustomerID         Diff       
##  Min.   :12346   Min.   :  0.00  
##  1st Qu.:13813   1st Qu.: 17.07  
##  Median :15300   Median : 50.09  
##  Mean   :15300   Mean   : 92.05  
##  3rd Qu.:16779   3rd Qu.:141.73  
##  Max.   :18287   Max.   :373.12
summary(monetary)
##    CustomerID     Total_price       
##  Min.   :12346   Min.   :     3.75  
##  1st Qu.:13813   1st Qu.:   307.42  
##  Median :15300   Median :   674.48  
##  Mean   :15300   Mean   :  2054.27  
##  3rd Qu.:16779   3rd Qu.:  1661.74  
##  Max.   :18287   Max.   :280206.02
  1. Distribution des variables :
    frequency (Fréquence des achats) : Min = 1 : Le client avec la fréquence minimale a effectué 1 achat. 1er Quartile (Q1) = 17 : 25% des clients ont effectué 17 achats ou moins. Médiane = 41 : La moitié des clients ont effectué 41 achats ou moins. Moyenne = 91.72 : En moyenne, les clients ont effectué environ 92 achats, ce qui peut être influencé par quelques clients très fréquents.3ème Quartile (Q3) = 100 : 75% des clients ont effectué moins de 100 achats.Max = 7847 : Il y a des clients avec un nombre extrêmement élevé d’achats (7847). Cela peut être une valeur extrême, à examiner pour vérifier si c’est une anomalie ou une transaction particulière.


recency (Récence des achats) : Min = 0 : Certains clients ont effectué un achat récemment (différence de 0 jours). 1er Quartile (Q1) = 17.07 : 25% des clients ont effectué leur dernier achat il y a moins de 17 jours. Médiane = 50.09 : La moitié des clients ont effectué leur dernier achat dans les 50 derniers jours. Moyenne = 92.05 : En moyenne, les clients ont effectué leur dernier achat il y a environ 92 jours, ce qui suggère que certains clients n’ont pas acheté récemment. Max = 373.12 : Le client le plus ancien a effectué son dernier achat il y a 373 jours (environ 12 mois). Ce client pourrait être considéré comme inactif.


monetary (Montant total dépensé) : Min = 3.75 : Le client ayant le montant minimal a dépensé 3.75.1er Quartile (Q1) = 307.42 : 25% des clients ont dépensé 307.42 ou moins. Médiane = 674.48 : La moitié des clients ont dépensé moins de 674.48. Moyenne = 2054.27 : En moyenne, les clients ont dépensé 2054.27, ce qui suggère que quelques clients ont des dépenses très élevées, ce qui influence la moyenne. 3ème Quartile (Q3) = 1661.74 : 75% des clients ont dépensé moins de 1661.74. Max = 280206.02 : Il y a des clients ayant dépensé une somme extrêmement élevée, 280206.02. Cela pourrait aussi être une valeur extrême qu’il conviendrait d’examiner.


Détection des valeurs aberrantes

J’ai calculé les valeurs monétaires (Monetary), de fréquence (Frequency) et de récence (Recency) regroupées par identifiant client. J’ai essayé de représenter ces valeurs sous forme de boîte à moustaches (box-plot). Ces données présentent des valeurs aberrantes qui pourraient affecter la précision des prédictions. Par conséquent, j’ai utilisé une mise à l’échelle standard (standard scaler) pour normaliser les données.

# Réaliser un box-plot pour plusieurs variables en même temps
RFM_long <- reshape2::melt(RFM, id.vars = "CustomerID", measure.vars = c("frequency", "recency", "monetary"))

ggplot(RFM_long, aes(x = variable, y = value, fill = variable)) +
  geom_boxplot() +
  labs(title = "Box-Plot des Variables RFM", x = "Variables", y = "Range") +
  theme_minimal() +
  theme(legend.position = "none")

La standardisation met chaque variable d’entrée sur une échelle séparée en soustrayant la moyenne (appelée centrage) et en divisant par l’écart-type, afin de décaler la distribution pour qu’elle ait une moyenne de zéro et un écart-type de un.

# Sélectionner les colonnes à normaliser
rfm_normalized <- RFM[, c('frequency', 'recency', 'monetary')]

# Appliquer la normalisation (centrage et réduction)
rfm_normalized <- scale(rfm_normalized)

# Afficher les données normalisées
head(rfm_normalized)
##        frequency    recency    monetary
## [1,] -0.39653199  2.3303706  8.35770470
## [2,]  0.39460347 -0.9016372  0.25093734
## [3,] -0.26540457 -0.1706302 -0.02859271
## [4,] -0.08182617 -0.7391518 -0.03300799
## [5,] -0.32659736  2.1778908 -0.19132522
## [6,] -0.02937520 -0.5611616  0.05025720
rfm_normalized_df <- as.data.frame(rfm_normalized)
summary(rfm_normalized_df$frequency)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -0.39653 -0.32660 -0.22170  0.00000  0.03619 33.89766
summary(rfm_normalized_df$monetary)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -0.22811 -0.19433 -0.15349  0.00000 -0.04367 30.94278
summary(rfm_normalized_df$recency)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## -0.9204 -0.7497 -0.4195  0.0000  0.4967  2.8104

Statistiques descriptives des variables normalisées : frequency :

Min : -0.3965 1st Qu. : -0.3266 Median : -0.2217 Mean : 0.0000 (c’est un bon signe que la normalisation a été correctement appliquée) Max : 33.8977 (ce nombre semble très élevé, il pourrait être utile de vérifier si ces valeurs extrêmes sont des cas rares ou des erreurs dans les données) recency :

Min : -0.2281 1st Qu. : -0.1943 Median : -0.1535 Mean : 0.0000 (c’est également une bonne indication de normalisation réussie) Max : 30.9428 (comme pour frequency, le max élevé pourrait signaler des valeurs extrêmes à vérifier) monetary :

Min : -0.9204 1st Qu. : -0.7497 Median : -0.4195 Mean : 0.0000 Max : 2.8104 (les valeurs sont désormais plus raisonnables ici) Interprétation générale : Moyenne proche de 0 et écart-type proche de 1 pour toutes les variables, ce qui confirme que la normalisation a bien été effectuée.

# Charger les bibliothèques nécessaires
library(ggplot2)
library(reshape2)
## 
## Attachement du package : 'reshape2'
## Les objets suivants sont masqués depuis 'package:data.table':
## 
##     dcast, melt
# Conversion du DataFrame en format long pour le box-plot
rfm_normalized_plot <- melt(rfm_normalized, measure.vars = c("frequency", "recency", "monetary"))

# Renommer la colonne 'Var2' en 'variable'
colnames(rfm_normalized_plot) <- c("Var1", "variable", "value")

head(rfm_normalized_plot)
##   Var1  variable       value
## 1    1 frequency -0.39653199
## 2    2 frequency  0.39460347
## 3    3 frequency -0.26540457
## 4    4 frequency -0.08182617
## 5    5 frequency -0.32659736
## 6    6 frequency -0.02937520
# Tracer le box-plot
ggplot(rfm_normalized_plot, aes(x = variable, y = value, fill = variable)) +
  geom_boxplot() +
  labs(title = "Box-Plot des Variables RFM", x = "Variables", y = "Plage des valeurs normalisées") +
  theme_minimal() +
  theme(legend.position = "none")

head(rfm_normalized_plot)
##   Var1  variable       value
## 1    1 frequency -0.39653199
## 2    2 frequency  0.39460347
## 3    3 frequency -0.26540457
## 4    4 frequency -0.08182617
## 5    5 frequency -0.32659736
## 6    6 frequency -0.02937520
summary(rfm_normalized_plot)
##       Var1           variable        value         
##  Min.   :   1   frequency:4338   Min.   :-0.92037  
##  1st Qu.:1085   recency  :4338   1st Qu.:-0.33971  
##  Median :2170   monetary :4338   Median :-0.18534  
##  Mean   :2170                    Mean   : 0.00000  
##  3rd Qu.:3254                    3rd Qu.: 0.03089  
##  Max.   :4338                    Max.   :33.89766
# Fonction pour traiter les outliers
remove_outliers <- function(data, column_name) {
  Q1 <- quantile(data[[column_name]], 0.25)
  Q3 <- quantile(data[[column_name]], 0.75)
  IQR <- Q3 - Q1
  
  # Définir les limites
  lower_limit <- Q1 - 1.5 * IQR
  upper_limit <- Q3 + 1.5 * IQR
  
  # Filtrer les données pour conserver uniquement les valeurs dans les limites
  data <- data[data[[column_name]] >= lower_limit & data[[column_name]] <= upper_limit, ]
  return(data)
}

# Appliquer la fonction sur chaque variable de rfm_normalized_plot
rfm_cleaned <- rfm_normalized_plot
rfm_cleaned <- remove_outliers(rfm_cleaned, "value")

# Vérifier le résultat
summary(rfm_cleaned)
##       Var1           variable        value        
##  Min.   :   1   frequency:3961   Min.   :-0.8925  
##  1st Qu.:1085   recency  :3071   1st Qu.:-0.3519  
##  Median :2172   monetary :4185   Median :-0.1998  
##  Mean   :2169                    Mean   :-0.2237  
##  3rd Qu.:3250                    3rd Qu.:-0.0862  
##  Max.   :4338                    Max.   : 0.5826
# Refaire le box-plot
ggplot(rfm_cleaned, aes(x = variable, y = value, fill = variable)) +
  geom_boxplot() +
  labs(title = "Box-Plot des Variables RFM (Après traitement des outliers)", 
       x = "Variables", 
       y = "Plage des valeurs normalisées") +
  theme_minimal() +
  theme(legend.position = "none")

#summary(rfm_normalized_plot)
# Calcul des seuils pour chaque variable
thresholds <- data.frame(
  variable = c("frequency", "monetary", "recency"),
  lower = sapply(rfm_normalized, function(x) quantile(x, 0.25) - 1.5 * IQR(x)),
  upper = sapply(rfm_normalized, function(x) quantile(x, 0.75) + 1.5 * IQR(x))
)

head(thresholds)
##    variable       lower       upper
## 1 frequency -0.39653199 -0.39653199
## 2  monetary  0.39460347  0.39460347
## 3   recency -0.26540457 -0.26540457
## 4 frequency -0.08182617 -0.08182617
## 5  monetary -0.32659736 -0.32659736
## 6   recency -0.02937520 -0.02937520

# 3. Exploration de la segmentation client avec l’analyse RFM

# Création des segments
rfm_normalized <- as.data.frame(rfm_normalized)

rfm_segmented <- rfm_normalized %>%
  mutate(
    segment_frequency = case_when(
      frequency < thresholds$lower[1] ~ "Outlier négatif",
      frequency > thresholds$upper[1] ~ "Outlier positif",
      TRUE ~ "Normal"
    ),
    segment_monetary = case_when(
      monetary < thresholds$lower[2] ~ "Outlier négatif",
      monetary > thresholds$upper[2] ~ "Outlier positif",
      TRUE ~ "Normal"
    ),
    segment_recency = case_when(
      recency < thresholds$lower[3] ~ "Outlier négatif",
      recency > thresholds$upper[3] ~ "Outlier positif",
      TRUE ~ "Normal"
    )
  )

3.1 Analyse des segments

Interprétation des segments :

segment_frequency :

“Normal” : Valeur de fréquence dans la moyenne. “Outlier positif” : Fréquence très élevée (client très actif).

segment_monetary :

“Outlier positif” : Client avec une valeur monétaire élevée (dépense beaucoup). “Outlier négatif” : Client avec une valeur monétaire très faible.

segment_recency :

“Outlier positif” : Client récemment actif (achat récent). “Outlier négatif” : Client qui n’a pas acheté depuis longtemps.

head(rfm_segmented)
##     frequency    recency    monetary segment_frequency segment_monetary
## 1 -0.39653199  2.3303706  8.35770470            Normal  Outlier positif
## 2  0.39460347 -0.9016372  0.25093734   Outlier positif  Outlier négatif
## 3 -0.26540457 -0.1706302 -0.02859271   Outlier positif  Outlier négatif
## 4 -0.08182617 -0.7391518 -0.03300799   Outlier positif  Outlier négatif
## 5 -0.32659736  2.1778908 -0.19132522   Outlier positif  Outlier négatif
## 6 -0.02937520 -0.5611616  0.05025720   Outlier positif  Outlier négatif
##   segment_recency
## 1 Outlier positif
## 2 Outlier négatif
## 3 Outlier positif
## 4 Outlier négatif
## 5 Outlier positif
## 6 Outlier négatif
table(rfm_segmented$segment_frequency)
## 
##          Normal Outlier positif 
##              71            4267
table(rfm_segmented$segment_monetary)
## 
## Outlier négatif Outlier positif 
##            4106             232
table(rfm_segmented$segment_recency)
## 
## Outlier négatif Outlier positif 
##            2511            1827
ggplot(rfm_segmented, aes(x = segment_frequency, fill = segment_frequency)) +
  geom_bar() +
  labs(title = "Distribution des Segments - Fréquence", x = "Segment", y = "Nombre de clients") +
  theme_minimal()

best_clients <- rfm_segmented %>% filter(segment_frequency == "Outlier positif")
summary(best_clients)
##    frequency           recency            monetary         segment_frequency 
##  Min.   :-0.39216   Min.   :-0.92037   Min.   :-0.227836   Length:4267       
##  1st Qu.:-0.32223   1st Qu.:-0.75022   1st Qu.:-0.193844   Class :character  
##  Median :-0.21732   Median :-0.42115   Median :-0.151953   Mode  :character  
##  Mean   : 0.00660   Mean   :-0.01815   Mean   : 0.001249                     
##  3rd Qu.: 0.04056   3rd Qu.: 0.44803   3rd Qu.:-0.040231                     
##  Max.   :33.89766   Max.   : 2.81036   Max.   :30.942777                     
##  segment_monetary   segment_recency   
##  Length:4267        Length:4267       
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
## 
at_risk_clients <- rfm_segmented %>% filter(segment_monetary == "Outlier négatif")
summary(at_risk_clients)
##    frequency           recency            monetary        segment_frequency 
##  Min.   :-0.39653   Min.   :-0.92037   Min.   :-0.22811   Length:4106       
##  1st Qu.:-0.33097   1st Qu.:-0.73025   1st Qu.:-0.19546   Class :character  
##  Median :-0.23481   Median :-0.38921   Median :-0.15919   Mode  :character  
##  Mean   :-0.09475   Mean   : 0.03994   Mean   :-0.11104                     
##  3rd Qu.:-0.01626   3rd Qu.: 0.58942   3rd Qu.:-0.07113                     
##  Max.   : 4.86168   Max.   : 2.81036   Max.   : 0.39403                     
##  segment_monetary   segment_recency   
##  Length:4106        Length:4106       
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
## 
ggplot(rfm_segmented, aes(x = segment_frequency, y = frequency, fill = segment_frequency)) +
  geom_boxplot() +
  labs(title = "Comparaison des Fréquences par Segment", x = "Segment", y = "Fréquence") +
  theme_minimal()

# 1. Préparation des données
rfm_normalized <- rfm_normalized[, c("frequency", "recency", "monetary")]

# 2. Déterminer le nombre optimal de clusters (méthode de l'inertie - Elbow method)
set.seed(123)
wss <- sapply(1:10, function(k) {
  kmeans(rfm_normalized, centers = k, nstart = 50)$tot.withinss
})
## Warning: Les étapes de transfer (quick-TRANSfer stage) ont dépassé le maximum
## (= 216900)
# Visualisation de l'inertie
plot(1:10, wss, type = "b", pch = 19, frame = FALSE, 
     xlab = "Nombre de clusters K",
     ylab = "Inertie totale (Within Sum of Squares)",
     main = "Méthode du coude pour choisir K")

# 3. Appliquer K-Means avec le nombre optimal de clusters (par exemple, K = 4)
set.seed(123)
kmeans_model <- kmeans(rfm_normalized, centers = 6, nstart = 50)

# 4. Ajouter les labels des clusters à vos données
rfm_segmented$cluster <- as.factor(kmeans_model$cluster)

# 5. Résumé des clusters
summary_clusters <- aggregate(rfm_normalized, by = list(Cluster = rfm_segmented$cluster), mean)
print(summary_clusters)
##   Cluster   frequency    recency    monetary
## 1       1 -0.29710770  2.0190546 -0.18246118
## 2       2 -0.05717003 -0.5849968 -0.07017814
## 3       3 24.98099546 -0.9060289  7.66150356
## 4       4  3.21311460 -0.8500352 21.00393314
## 5       5 -0.23413698  0.6097588 -0.14924641
## 6       6  1.75188535 -0.7631732  1.13869977
# 6. Visualisation des clusters (exemple : graphique en 2D avec PCA)

pca_res <- prcomp(rfm_normalized, scale = TRUE)
pca_data <- data.frame(pca_res$x[, 1:2], Cluster = rfm_segmented$cluster)

ggplot(pca_data, aes(x = PC1, y = PC2, color = Cluster)) +
  geom_point(alpha = 0.6, size = 3) +
  labs(title = "Visualisation des Clusters K-Means",
       x = "Composante Principale 1",
       y = "Composante Principale 2") +
  theme_minimal()


Cluster Frequency (Fréquence) Recency (Récence) Monetary (Valeur Monétaire) Profil probable
1 Faible Très récent Faible Nouveaux clients ou clients irréguliers
2 Faible Moyennement récent Faible Clients dormants (achats peu fréquents et de faible valeur)
3 Très élevé (24.98) Ancien Très élevé (7.66) Clients fidèles avec des dépenses élevées
4 Élevé (3.21) Ancien Très élevé (21.00) Clients VIP avec une forte valeur mais inactifs récemment
5 Faible Moyennement actif Faible Clients standards (ni trop actifs, ni trop dépensiers)
6 Moyen à élevé Récemment actif Moyen Clients engagés, mais avec une valeur monétaire modérée

Voici quelques applications métiers basées sur l’analyse RFM et le clustering des clients :

  1. Segmentation et Personnalisation Marketing Campagnes ciblées : Envoi d’e-mails promotionnels aux clients les plus actifs (Cluster 3 et 4) pour les fidéliser. Réactivation des clients dormants : Relancer les clients du Cluster 2 et 5 avec des offres attractives.
  2. Optimisation des Programmes de Fidélité Récompenses adaptées : Proposer des remises aux clients les plus rentables (Cluster 4). Offres spécifiques aux petits acheteurs : Encourager les clients du Cluster 1 avec des offres adaptées à leur panier moyen.
  3. Gestion des Stocks et Approvisionnement Prévision de la demande : Ajuster les stocks en fonction des tendances d’achat des clusters. Produits phares : Identifier les articles préférés des meilleurs clients (Cluster 3 et 4) pour éviter les ruptures de stock.
  4. Amélioration de l’Expérience Client Support client priorisé : Offrir un service premium aux clients les plus fidèles et rentables. Personnalisation du parcours utilisateur : Adapter l’expérience en ligne en fonction du cluster du client.
  5. Optimisation des Tarifs et Promotions Stratégie de prix différenciée : Tester des promotions sur des segments spécifiques pour maximiser la conversion. Up-selling et Cross-selling : Proposer des recommandations personnalisées basées sur les habitudes d’achat des clusters.